Add lowrance usr support from Jason Rust/jrust at rustyparts.com.
authorrobertl <robertl@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Sat, 12 Mar 2005 05:27:30 +0000 (05:27 +0000)
committerrobertl <robertl@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Sat, 12 Mar 2005 05:27:30 +0000 (05:27 +0000)
gpsbabel/Makefile
gpsbabel/README
gpsbabel/lowranceusr.c [new file with mode: 0644]
gpsbabel/reference/lowrance.usr [new file with mode: 0644]
gpsbabel/testo
gpsbabel/vecs.c

index 7dd93ec342a2df2146df6a4ae52641e14b4c00ce..cb2137150009b9c81e7b14b0f3132d83ccf1170a 100644 (file)
@@ -27,7 +27,7 @@ FMTS=magproto.o gpx.o geo.o mapsend.o mapsource.o garmin_tables.o \
        gpilots.o saroute.o navicache.o psitrex.o geoniche.o delgpl.o \
        ozi.o nmea.o text.o html.o palmdoc.o netstumbler.o hsa_ndv.o \
        igc.o brauniger_iq.o shape.o hiketech.o glogbook.o coastexp.o \
-       vcf.o overlay.o google.o
+       vcf.o overlay.o google.o lowranceusr.o
 
 FILTERS=position.o duplicate.o arcdist.o polygon.o smplrout.o reverse_route.o sort.o stackfilter.o
 
@@ -170,6 +170,7 @@ html.o: html.c defs.h queue.h gbtypes.h jeeps/gpsmath.h jeeps/gps.h \
   jeeps/gpsinput.h jeeps/gpsproj.h jeeps/gpsnmeafmt.h jeeps/gpsnmeaget.h
 igc.o: igc.c defs.h queue.h gbtypes.h
 internal_styles.o: internal_styles.c defs.h queue.h gbtypes.h
+lowranceusr.o: lowranceusr.c defs.h queue.h gbtypes.h
 magnav.o: magnav.c defs.h queue.h gbtypes.h coldsync/palm.h \
   coldsync/pdb.h
 magproto.o: magproto.c defs.h queue.h gbtypes.h magellan.h
index 30c425e83029edb3937f02f9499d69d9e619ddf7..831a5ac6bae81d83ce0a0128cb0db7bcf7541d0d 100644 (file)
@@ -134,6 +134,14 @@ THE FORMATS
         manager) for iFinder which is available at 
        http://www.lowrance.com/Software/GDM6/Default.asp
 
+    Lowrance USR
+
+    The Lowrance iFinder GPS series has the unique capability to output 
+    its data to an MMC card.  The data is saved to the card as a .USR  
+    file and can be read by your computer using a card reader. 
+    Currently only reading and writing of waypoints (no routes or 
+    tracks) is supported.
+
     XMap
 
        Delorme TopoUSA/XMap Conduit is one of the billion CSV variants 
diff --git a/gpsbabel/lowranceusr.c b/gpsbabel/lowranceusr.c
new file mode 100644 (file)
index 0000000..9a1a233
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+       Access to Lowrance USR files.
+       Contributed to gpsbabel by Jason Rust (jrust at rustyparts.com)
+
+       Copyright (C) 2005 Robert Lipe, robertlipe@usa.net
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation; either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with this program; if not, write to the Free Software
+       Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+*/
+
+
+#include "defs.h"
+#include <string.h>
+#include <math.h> /* for lat/lon conversion */
+
+typedef struct lowranceusr_icon_mapping {
+       const long int  value;
+       const char              *icon;
+} lowranceusr_icon_mapping_t;
+
+/* Taken from iFinder 1.8 */
+const lowranceusr_icon_mapping_t lowranceusr_icon_value_table[] = {
+       { 10000, "diamond 1" },
+       { 10001, "diamond 2" },
+       { 10002, "diamond 3" },
+       { 10003, "x 1" },
+       { 10004, "x 2" },
+       { 10005, "x 3" },
+       { 10006, "cross" },
+       { 10007, "house" },
+       { 10008, "car" },
+       { 10009, "store" },
+       { 10010, "gas station" },
+       { 10011, "fork and spoon" },
+       { 10012, "telephone" },
+       { 10013, "airplane" },
+       { 10014, "exit sign" },
+       { 10015, "stop sign" },
+       { 10016, "exclamation" },
+       { 10017, "traffic light" },
+       { 10018, "american flag" },
+       { 10019, "person" },
+       { 10020, "restrooms" },
+       { 10021, "tree" },
+       { 10022, "mountains" },
+       { 10023, "campsite" },
+       { 10024, "picnic table" },
+       { 10025, "deer" },
+       { 10026, "deer tracks" },
+       { 10027, "turkey tracks" },
+       { 10028, "tree stand" },
+       { 10029, "bridge" },
+       { 10030, "skull and crossbones" },
+       { 10031, "fish" },
+       { 10032, "two fish" },
+       { 10033, "dive flag" },
+       { 10034, "wreck" },
+       { 10035, "anchor" },
+       { 10036, "boat" },
+       { 10037, "boat ramp" },
+       { 10038, "flag buoy" },
+       { 10039, "dam" },
+       { 10040, "swimmer" },
+       { 10041, "pier"},
+
+       { 10038, "Micro-Cache" },       /* icon for "flag buoy" */
+       { 10030, "Virtual cache" },     /* icon for "skull and crossbones" */
+       { 10032, "Multi-Cache" },       /* icon for "two fish" */
+       { 10003, "Unknown Cache" },     /* icon for "x 1" */
+       { 10018, "Locationless (Reverse) Cache" }, /* Icon for "american flag" */
+       { 10007, "Post Office" },       /* Icon for "house" */
+       { 10019, "Event Cache" },       /* Icon for "person" */
+       { 10020, "Webcam Cache" },      /* Icon for "restrooms" */
+       {        -1, NULL }
+};
+
+static FILE *file_in;
+static FILE *file_out;
+static void *mkshort_handle;
+
+static unsigned int waypt_out_count;
+
+#define MYNAME "Lowrance USR"
+
+#define MAXUSRSTRINGSIZE       256
+#define SEMIMINOR                 6356752.3142
+#define DEGREESTORADIANS       0.017453292
+#define SECSTO2000               946713599
+
+const char *
+lowranceusr_find_desc_from_icon_number(const int icon)
+{
+       const lowranceusr_icon_mapping_t *i;
+
+       for (i = lowranceusr_icon_value_table; i->icon; i++) {
+               if (icon == i->value) {
+                       return i->icon;
+               }
+       }
+
+       return "";
+}
+
+long int
+lowranceusr_find_icon_number_from_desc(const char *desc)
+{
+       const lowranceusr_icon_mapping_t *i;
+       long int def_icon = 10001;
+
+       if (!desc) {
+               return def_icon;
+       }
+
+       for (i = lowranceusr_icon_value_table; i->icon; i++) {
+               if (case_ignore_strcmp(desc,i->icon) == 0) {
+                       return i->value;
+               }
+       }
+
+       return def_icon;
+}
+static int
+lowranceusr_fread(void *buff, size_t size, size_t members, FILE * fp) 
+{
+       size_t br;
+
+       br = fread(buff, size, members, fp);
+
+       if (br != members) {
+               fatal(MYNAME ": requested to read %d bytes, read %d bytes.\n", members, br);
+       }
+
+       return (br);
+}
+
+static
+arglist_t lowranceusr_args[] = {
+       {0, 0, 0, 0,0 }
+};
+
+static void
+rd_init(const char *fname)
+{
+       file_in = xfopen(fname, "rb", MYNAME);
+}
+
+static void
+rd_deinit(void)
+{
+       fclose(file_in);
+}
+
+static void
+wr_init(const char *fname)
+{
+       file_out = xfopen(fname, "wb", MYNAME);
+       mkshort_handle = mkshort_new_handle();
+       waypt_out_count = 0;
+}
+
+static void
+wr_deinit(void)
+{
+       fclose(file_out);
+       mkshort_del_handle(mkshort_handle);
+}
+
+/**
+ * Latitude and longitude for USR coords are in the lowrance mercator meter
+ * format in WGS84.  The below code converts them to degrees.
+ */
+static double 
+lon_mm_to_deg(double x) {
+       return x / (DEGREESTORADIANS * SEMIMINOR);
+}
+
+static long 
+lon_deg_to_mm(double x) {
+       return (long)(x * SEMIMINOR * DEGREESTORADIANS);
+}
+
+static double 
+lat_mm_to_deg(double x) {
+       return (2 * atan(exp(x / SEMIMINOR)) - M_PI / 2) / DEGREESTORADIANS;
+}
+
+static long
+lat_deg_to_mm(double x) {
+       return (long)(SEMIMINOR * log(tan((x * DEGREESTORADIANS + M_PI / 2) / 2)));
+}
+
+static void
+data_read(void)
+{
+       char buff[MAXUSRSTRINGSIZE + 1];
+       short int NumWaypoints, MajorVersion, MinorVersion;
+       int i;
+       long int TextLen;
+
+       lowranceusr_fread(&buff[0], 2, 1, file_in);
+       MajorVersion = le_read16(&buff[0]);
+       lowranceusr_fread(&buff[0], 2, 1, file_in);
+       MinorVersion = le_read16(&buff[0]);
+       
+       if (MajorVersion < 2) {
+               fatal(MYNAME ": input file is from an old version of the USR file and is not supported\n");
+       }
+
+       lowranceusr_fread(&buff[0], 2, 1, file_in);
+       NumWaypoints = le_read16(&buff[0]);
+       for (i = 0; i < NumWaypoints; i++) {
+               waypoint *wpt_tmp;
+
+               wpt_tmp = waypt_new();
+
+               /* Object num */
+               lowranceusr_fread(&buff[0], 2, 1, file_in);
+               lowranceusr_fread(&buff[0], 4, 1, file_in);
+               wpt_tmp->latitude = lat_mm_to_deg(le_read32(&buff[0]));
+               lowranceusr_fread(&buff[0], 4, 1, file_in);
+               wpt_tmp->longitude = lon_mm_to_deg(le_read32(&buff[0]));
+               lowranceusr_fread(&buff[0], 4, 1, file_in);
+               wpt_tmp->altitude = le_read32(&buff[0]);
+
+               lowranceusr_fread(&buff[0], 4, 1, file_in);
+               TextLen = buff[0];
+               lowranceusr_fread(&buff[0], TextLen, 1, file_in);
+               buff[TextLen] = '\0';
+               wpt_tmp->shortname = xstrdup(buff);
+
+               lowranceusr_fread(&buff[0], 4, 1, file_in);
+               TextLen = buff[0];
+               if (TextLen) {
+                       lowranceusr_fread(&buff[0], TextLen, 1, file_in);
+                       buff[TextLen] = '\0';
+                       wpt_tmp->description = xstrdup(buff);
+               }
+
+               lowranceusr_fread(&buff[0], 4, 1, file_in);
+               /* Time is number of seconds since Jan. 1, 2000 */
+               wpt_tmp->creation_time = SECSTO2000 + le_read32(&buff[0]);
+
+               /* Symbol ID */
+               lowranceusr_fread(&buff[0], 4, 1, file_in);
+               wpt_tmp->icon_descr = lowranceusr_find_desc_from_icon_number(le_read32(&buff[0]));
+
+               /* Waypoint Type (USER, TEMPORARY, POINT_OF_INTEREST) */
+               lowranceusr_fread(&buff[0], 2, 1, file_in);
+
+               waypt_add(wpt_tmp);
+       }
+}
+
+static void
+lowranceusr_waypt_pr(const waypoint *wpt)
+{
+       long int TextLen, Lat, Lon, Time, SymbolId;
+       short int WayptType;
+       char *name;
+       char *comment;
+
+       /* our personal waypoint counter */
+       fwrite(&waypt_out_count, 2, 1, file_out);
+       waypt_out_count++;
+
+       Lat = lat_deg_to_mm(wpt->latitude);
+       fwrite(&Lat, 4, 1, file_out);
+       Lon = lon_deg_to_mm(wpt->longitude);
+       fwrite(&Lon, 4, 1, file_out);
+       fwrite(&wpt->altitude, 4, 1, file_out);
+
+       /* Try and make sure we have a name */
+       if ((! wpt->shortname) || global_opts.synthesize_shortnames) {
+               if (wpt->description && global_opts.synthesize_shortnames) {
+                       name = mkshort(mkshort_handle, wpt->description);
+               } else if (wpt->shortname) {
+                       name = xstrdup(wpt->shortname);
+               } else if (wpt->description) {
+                       name = xstrdup(wpt->description);
+               } else {
+                       name = xstrdup("");
+               }
+       } else {
+               name = xstrdup(wpt->shortname);
+       }
+
+       TextLen = strlen(name);
+       fwrite(&TextLen, 4, 1, file_out);
+       fwrite(name, 1, TextLen, file_out);
+       xfree(name);
+
+       /**
+        * Comments aren't used by the iFinder yet so they just take up space...
+        */
+       if (0 && wpt->description && strcmp(wpt->description, wpt->shortname) != 0) {
+               comment = xstrdup(wpt->description);
+               TextLen = strlen(comment);
+               fwrite(&TextLen, 4, 1, file_out);
+               fwrite(comment, 1, TextLen, file_out);
+               xfree(comment);
+       } else {
+               TextLen = 0;
+               fwrite(&TextLen, 4, 1, file_out);
+       }
+
+       if (wpt->creation_time > SECSTO2000) {
+               Time = wpt->creation_time - SECSTO2000;
+       } else {
+               Time = SECSTO2000 + 1;
+       }
+       fwrite(&Time, 4, 1, file_out);
+
+       if (get_cache_icon(wpt) && wpt->icon_descr && (strcmp(wpt->icon_descr, "Geocache Found") != 0)) {
+               SymbolId = lowranceusr_find_icon_number_from_desc(get_cache_icon(wpt));
+       } else {
+               SymbolId = lowranceusr_find_icon_number_from_desc(wpt->icon_descr);
+       }
+
+       fwrite(&SymbolId, 4, 1, file_out);
+
+       /* USER waypoint type */
+       WayptType = 0;
+       fwrite(&WayptType, 2, 1, file_out);
+}
+
+static void
+data_write(void)
+{
+       short int NumWaypoints, MajorVersion, MinorVersion, NumRoutes, NumTrails, NumIcons;
+       setshort_length(mkshort_handle, 15);
+       MajorVersion = 2;
+       MinorVersion = 0;
+       NumWaypoints = waypt_count();
+
+       fwrite(&MajorVersion, 2, 1, file_out);
+       fwrite(&MinorVersion, 2, 1, file_out);
+       fwrite(&NumWaypoints, 2, 1, file_out);
+       waypt_disp_all(lowranceusr_waypt_pr);
+
+       /* We don't support these yet... */
+       NumRoutes = 0;
+       fwrite(&NumRoutes, 2, 1, file_out);
+       NumIcons = 0;
+       fwrite(&NumIcons, 2, 1, file_out);
+       NumTrails = 0;
+       fwrite(&NumTrails, 2, 1, file_out);
+}
+
+
+ff_vecs_t lowranceusr_vecs = {
+       ff_type_file,
+       FF_CAP_RW_WPT,
+       rd_init,
+       wr_init,
+       rd_deinit,
+       wr_deinit,
+       data_read,
+       data_write,
+       NULL, 
+       lowranceusr_args
+};
diff --git a/gpsbabel/reference/lowrance.usr b/gpsbabel/reference/lowrance.usr
new file mode 100644 (file)
index 0000000..1982f8a
Binary files /dev/null and b/gpsbabel/reference/lowrance.usr differ
index fa01bc0f15e7936ebce6ef99bcb690c93b1f04ed..d8646aecb59114ad206236b98e4a1e37c8589bc0 100755 (executable)
@@ -80,6 +80,17 @@ ${PNAME} -i geo -f geocaching.loc -o tiger -F ${TMPDIR}/tiger
 ${PNAME} -i tiger -f ${TMPDIR}/tiger -o tiger -F ${TMPDIR}/tiger2
 compare ${TMPDIR}/tiger ${TMPDIR}/tiger2
 
+#
+# Lowrance USR.  Binary, and also slightly lossy because of the math to
+# convert lat/long.
+#
+rm -f ${TMPDIR}/lowrance1.usr
+${PNAME} -i geo -f geocaching.loc -o lowranceusr -F ${TMPDIR}/lowrance1.usr
+bincompare ${TMPDIR}/lowrance1.usr reference/lowrance.usr
+${PNAME} -i lowranceusr -f ${TMPDIR}/lowrance1.usr -o gpx -o ${TMPDIR}/lowrance1.out
+${PNAME} -i geo -f geocaching.loc -o gpx -F ${TMPDIR}/lowrance2.out
+compare ${TMPDIR}/lowrance1.out ${TMPDIR}/lowrance2.out
+
 # CSV (Comma separated value) data.
 
 ${PNAME}  -i geo -f geocaching.loc -o csv -F ${TMPDIR}/csv.csv
index 83dbab31318600260c728af80a46f4a4aa478806..bbc0e64305be941bc6617f1cb7aba938d19cc079 100644 (file)
@@ -39,6 +39,7 @@ extern ff_vecs_t mps_vecs;
 extern ff_vecs_t gpsutil_vecs;
 extern ff_vecs_t tiger_vecs;
 extern ff_vecs_t pcx_vecs;
+extern ff_vecs_t lowranceusr_vecs;
 extern ff_vecs_t cetus_vecs;
 extern ff_vecs_t gpspilot_vecs;
 extern ff_vecs_t copilot_vecs;
@@ -138,6 +139,12 @@ vecs_t vec_list[] = {
                "MS PocketStreets 2002 Pushpin",
                "psp"
        },
+       {
+               &lowranceusr_vecs,
+               "lowranceusr",
+               "Lowrance USR",
+               NULL
+       },
        {
                &cetus_vecs,
                "cetus",